
#include "stdafx.h"
#include "ButtonUIEx.h"

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if (p) { delete (p); (p) = 0; } }
#endif // SAFE_DELETE

IMPLEMENT_DUICONTROL(CButtonExUI)
/////////////////////////////////////////////////////////////////////////////////////
//
//

CButtonExUI::CButtonExUI()
	: CButtonUI()
	, m_pMaskImage(NULL)
	, m_pBmpFluffy(NULL)
	, m_pBmpMask(NULL)
{
	m_nFramePosition = 0;
	m_nFrameCount = 0;
	m_nTimerSize = 60;
	m_bLeaveFlag = false;
	m_bNormalFlag = true;
	::ZeroMemory(&m_rcPushedTextPadding, sizeof(m_rcPushedTextPadding));
}

CButtonExUI::~CButtonExUI()
{
	SAFE_DELETE(m_pMaskImage);
	SAFE_DELETE(m_pBmpFluffy);
	SAFE_DELETE(m_pBmpMask);
}

void CButtonExUI::SetMaskImage(LPCTSTR pStrImage)
{
	m_sMaskImage = pStrImage;
	Invalidate();
}

LPCTSTR CButtonExUI::GetMaskImage()
{
	return m_sMaskImage;
}

LPCTSTR CButtonExUI::GetPushedForeImage()
{
	return m_sPushedForeImage;
}

void CButtonExUI::SetPushedForeImage(LPCTSTR pStrImage)
{
	m_sPushedForeImage = pStrImage;
	Invalidate();
}

void CButtonExUI::SetAnimationImages(vector<DuiLib::CDuiString> vecList)
{
	vecAnimation = vecList;
	m_nFrameCount = vecList.size();
}

RECT CButtonExUI::GetPushedTextPadding()
{
	return m_rcPushedTextPadding;
}

void CButtonExUI::SetPushedTextPadding(RECT rc)
{
	m_rcPushedTextPadding = rc;
	Invalidate();
}

void CButtonExUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
{
	if (_tcscmp(pstrName, _T("maskimage")) == 0) SetMaskImage(pstrValue);
	else if (_tcsicmp(pstrName, _T("pushedforeimage")) == 0) SetPushedForeImage(pstrValue);
	else if (_tcsicmp(pstrName, _T("pushedtextpadding")) == 0) {
		RECT rcTextPadding = { 0 };
		LPTSTR pstr = NULL;
		rcTextPadding.left = _tcstol(pstrValue, &pstr, 10);  ASSERT(pstr);
		rcTextPadding.top = _tcstol(pstr + 1, &pstr, 10);    ASSERT(pstr);
		rcTextPadding.right = _tcstol(pstr + 1, &pstr, 10);  ASSERT(pstr);
		rcTextPadding.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);
		SetPushedTextPadding(rcTextPadding);
	}
	else CButtonUI::SetAttribute(pstrName, pstrValue);
}

bool CButtonExUI::DoPaint(HDC hDC, const RECT& rcPaint, CControlUI* pStopControl)
{
	if (!::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem)) return true;

	// RESERVED: add your code here.
	if (m_bNormalFlag && vecAnimation.size() > 0)
		DrawImage(hDC, (LPCTSTR)vecAnimation[0]);

	if ((m_uButtonState & UISTATE_HOT) != 0)
	{
		DrawFrame(hDC);
		m_bNormalFlag = false;
	}
	if (m_bLeaveFlag)
		DrawLeaveFrame(hDC);

	return __super::DoPaint(hDC, rcPaint, pStopControl);
}

void CButtonExUI::PaintBkImage(HDC hDC)
{
	if (m_sMaskImage && m_sBkImageEx != m_sBkImage) {
		do {
			m_sBkImageEx = m_sBkImage;

			SAFE_DELETE(m_pBmpFluffy);
			m_pBmpFluffy = new Gdiplus::Bitmap(m_pManager->GetResourcePath() + m_sBkImageEx);
			if (!m_pBmpFluffy || m_pBmpFluffy->GetLastStatus() != Gdiplus::Status::Ok) {
				// TODO: check if m_sBkImageEx contains an absolute path.
				m_pBmpFluffy = new Gdiplus::Bitmap(m_sBkImageEx);
				if (!m_pBmpFluffy || m_pBmpFluffy->GetLastStatus() != Gdiplus::Status::Ok) {
					break;
				}
			}

			if (m_sMaskImage.IsEmpty()) {
				break;
			}

			SAFE_DELETE(m_pMaskImage);
			m_pMaskImage = Gdiplus::Image::FromFile(m_pManager->GetResourcePath() + m_sMaskImage);
			if (!m_pMaskImage || m_pMaskImage->GetLastStatus() != Gdiplus::Status::Ok) {
				break;
			}

			if (!_ResizeClone(m_pMaskImage, m_pBmpFluffy->GetWidth(), m_pBmpFluffy->GetHeight())) {
				break;
			}

			Gdiplus::Rect rectFluffy(0, 0, m_pBmpFluffy->GetWidth(), m_pBmpFluffy->GetHeight());

			SAFE_DELETE(m_pBmpMask);
			m_pBmpMask = new Gdiplus::Bitmap(rectFluffy.Width, rectFluffy.Height);
			if (!m_pBmpMask) {
				break;
			}

			Gdiplus::Graphics graphics(m_pBmpMask);
			graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
			graphics.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
			graphics.DrawImage(m_pMaskImage, rectFluffy);

			TransferOneARGBChannelFromOneBitmapToAnother(*m_pBmpMask, *m_pBmpFluffy, ChannelARGB::Blue, ChannelARGB::Alpha);
			SAFE_DELETE(m_pBmpMask);
		} while (0);
	}

	if (m_pBmpFluffy) {
		Gdiplus::Rect rectDst(m_rcPaint.left, m_rcPaint.top, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top);
		Gdiplus::Rect rectSrc((m_rcPaint.left - m_rcItem.left) * m_pBmpFluffy->GetWidth() / (m_rcItem.right - m_rcItem.left),
			(m_rcPaint.top - m_rcItem.top) * m_pBmpFluffy->GetHeight() / (m_rcItem.bottom - m_rcItem.top),
			(m_rcPaint.right - m_rcPaint.left) * m_pBmpFluffy->GetWidth() / (m_rcItem.right - m_rcItem.left),
			(m_rcPaint.bottom - m_rcPaint.top) * m_pBmpFluffy->GetHeight() / (m_rcItem.bottom - m_rcItem.top));

		Gdiplus::Graphics g(hDC);
		g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
		g.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
		g.DrawImage(m_pBmpFluffy, rectDst, rectSrc.X, rectSrc.Y, rectSrc.Width, rectSrc.Height, Gdiplus::Unit::UnitPixel);
	}
}

bool CButtonExUI::TransferOneARGBChannelFromOneBitmapToAnother(IN Gdiplus::Bitmap &src,
	OUT Gdiplus::Bitmap &dst,
	ChannelARGB srcChannel,
	ChannelARGB dstChannel)
{
	if (src.GetWidth() != dst.GetWidth() || src.GetHeight() != dst.GetHeight())
		return false;

	Gdiplus::Rect rectSrc(0, 0, src.GetWidth(), src.GetHeight());
	Gdiplus::BitmapData bdSrc, bdDst;
	src.LockBits(&rectSrc, Gdiplus::ImageLockMode::ImageLockModeRead, PixelFormat32bppPARGB, &bdSrc);
	dst.LockBits(&rectSrc, Gdiplus::ImageLockMode::ImageLockModeWrite, PixelFormat32bppPARGB, &bdDst);

	// unsafe codes that protected by LockBits
	byte *bpSrc = (byte *)bdSrc.Scan0;
	byte *bpDst = (byte *)bdDst.Scan0;

	bpSrc += (int)srcChannel;
	bpDst += (int)dstChannel;
	for (int i = rectSrc.Height * rectSrc.Width; i > 0; i--) {
		*bpDst = *bpSrc;
		bpSrc += 4;
		bpDst += 4;
	}
	// end (unsafe codes)

	src.UnlockBits(&bdSrc);
	dst.UnlockBits(&bdDst);
	return true;
}

bool CButtonExUI::_ResizeClone(Gdiplus::Image *&image, INT width, INT height)
{
	UINT image_width = image->GetWidth();
	UINT image_height = image->GetHeight();
	INT width_ = width;
	INT height_ = height;

	if (image_width == width_ && image_height == height_)
		return true;

	double ratio = ((double)image_width) / image_height;
	if (image_width > image_height) { // Resize down by width
		height_ = static_cast<UINT>(((double)width_) / ratio);
	}
	else {
		width_ = static_cast<UINT>(((double)height_) / ratio);
	}

	Gdiplus::Image *dst = new Gdiplus::Bitmap(width_, height_, image->GetPixelFormat());
	if (dst && dst->GetLastStatus() == Gdiplus::Status::Ok) {
		Gdiplus::Graphics graphics(dst);
		graphics.DrawImage(image, 0, 0, width_, height_);

		SAFE_DELETE(image);
		image = dst;
		return true;
	}

	SAFE_DELETE(dst);
	return false;
}

void CButtonExUI::PaintStatusImage(HDC hDC)
{
	if (IsFocused()) m_uButtonState |= UISTATE_FOCUSED;
	else m_uButtonState &= ~UISTATE_FOCUSED;
	if (!IsEnabled()) m_uButtonState |= UISTATE_DISABLED;
	else m_uButtonState &= ~UISTATE_DISABLED;

	if (!::IsWindowEnabled(m_pManager->GetPaintWindow())) {
		m_uButtonState &= UISTATE_DISABLED;
	}
	if ((m_uButtonState & UISTATE_DISABLED) != 0) {
		if (!m_sDisabledImage.IsEmpty())
		{
			if (!DrawImage(hDC, (LPCTSTR)m_sDisabledImage)) m_sDisabledImage.Empty();
			else goto Label_ForeImage;
		}
	}
	else if ((m_uButtonState & UISTATE_PUSHED) != 0) {
		if (!m_sPushedImage.IsEmpty()) {
			if (!DrawImage(hDC, (LPCTSTR)m_sPushedImage)) {
				m_sPushedImage.Empty();
			}
			if (!m_sPushedForeImage.IsEmpty())
			{
				if (!DrawImage(hDC, (LPCTSTR)m_sPushedForeImage))
					m_sPushedForeImage.Empty();
				return;
			}
			else goto Label_ForeImage;
		}
		else if (m_dwPushedBkColor != 0) {
			CRenderEngine::DrawColor(hDC, m_rcPaint, GetAdjustColor(m_dwPushedBkColor));
			return;
		}
	}
	else if ((m_uButtonState & UISTATE_HOT) != 0) {
		if (!m_sHotImage.IsEmpty())
		{
			if (!DrawImage(hDC, (LPCTSTR)m_sHotImage))
			{
				m_sHotImage.Empty();
			}
			if (!m_sHotForeImage.IsEmpty())
			{
				if (!DrawImage(hDC, (LPCTSTR)m_sHotForeImage))
					m_sHotForeImage.Empty();
				return;
			}
			else goto Label_ForeImage;
		}
		else if (m_nFrameCount != 0)
		{
			m_pManager->SetTimer(this, EVENT_ANIMA_ENTER_TIMERID, m_nTimerSize);
		}
		else if (m_dwHotBkColor != 0) {
			CRenderEngine::DrawColor(hDC, m_rcPaint, GetAdjustColor(m_dwHotBkColor));
			return;
		}
	}
	else if ((m_uButtonState & UISTATE_FOCUSED) != 0) {
		if (!m_sFocusedImage.IsEmpty()) {
			if (!DrawImage(hDC, (LPCTSTR)m_sFocusedImage)) m_sFocusedImage.Empty();
			else goto Label_ForeImage;
		}
	}

	if (!m_sNormalImage.IsEmpty()) {
		if (!DrawImage(hDC, (LPCTSTR)m_sNormalImage)) m_sNormalImage.Empty();
		else goto Label_ForeImage;
	}

	if (!m_sForeImage.IsEmpty())
		goto Label_ForeImage;

	return;

Label_ForeImage:
	if (!m_sForeImage.IsEmpty()) {
		if (!DrawImage(hDC, (LPCTSTR)m_sForeImage)) m_sForeImage.Empty();
	}
}

void CButtonExUI::DoEvent(TEventUI& event)
{
	CButtonUI::DoEvent(event);
	if (event.Type == UIEVENT_MOUSELEAVE)
	{
		if (IsEnabled()) {
			m_uButtonState &= ~UISTATE_HOT;
			Invalidate();
			if (m_pManager->KillTimer(this, EVENT_ANIMA_ENTER_TIMERID))
			{
				m_bLeaveFlag = true;
				m_pManager->SetTimer(this, EVENT_ANIMA_LEAVE_TIMERID, m_nTimerSize);
			}
		}
	}
	if (event.Type == UIEVENT_TIMER)
	{
		if ((UINT_PTR)event.wParam == EVENT_ANIMA_ENTER_TIMERID)
			OnTimer((UINT_PTR)event.wParam);
		else if ((UINT_PTR)event.wParam == EVENT_ANIMA_LEAVE_TIMERID)
			OnLeaveTimer((UINT_PTR)event.wParam);
	}
}

void CButtonExUI::OnTimer(UINT_PTR idEvent)
{
	if (idEvent != EVENT_ANIMA_ENTER_TIMERID)
		return;
	m_pManager->KillTimer(this, EVENT_ANIMA_ENTER_TIMERID);
	this->Invalidate();

	m_nFramePosition++;

	m_pManager->SetTimer(this, EVENT_ANIMA_ENTER_TIMERID, m_nTimerSize);
}

void CButtonExUI::DrawFrame(HDC hDC)
{
	if (NULL == hDC || vecAnimation.size() == 0 || m_nFramePosition >= m_nFrameCount)
		return;
	DrawImage(hDC, (LPCTSTR)vecAnimation[m_nFramePosition]);
	if (m_nFramePosition + 1 == m_nFrameCount)
	{
		m_pManager->KillTimer(this, EVENT_ANIMA_ENTER_TIMERID);
		DrawImage(hDC, (LPCTSTR)vecAnimation[m_nFramePosition]);
		this->Invalidate();
	}
}

void CButtonExUI::OnLeaveTimer(UINT_PTR idEvent)
{
	if (idEvent != EVENT_ANIMA_LEAVE_TIMERID)
		return;
	m_pManager->KillTimer(this, EVENT_ANIMA_LEAVE_TIMERID);
	m_pManager->KillTimer(this, EVENT_ANIMA_ENTER_TIMERID);
	this->Invalidate();

	m_nFramePosition--;
	m_pManager->SetTimer(this, EVENT_ANIMA_LEAVE_TIMERID, m_nTimerSize);
}

void CButtonExUI::DrawLeaveFrame(HDC hDC)
{
	if (NULL == hDC || vecAnimation.size() == 0)
		return;
	DrawImage(hDC, (LPCTSTR)vecAnimation[m_nFramePosition]);
	if (m_nFramePosition == 0)
	{
		m_pManager->KillTimer(this, EVENT_ANIMA_LEAVE_TIMERID);
		DrawImage(hDC, (LPCTSTR)vecAnimation[m_nFramePosition]);
		this->Invalidate();
		m_bLeaveFlag = false;
		m_bNormalFlag = true;
	}
}

void CButtonExUI::PaintText(HDC hDC)
{
	if ((m_uButtonState & UISTATE_PUSHED) != 0 && (m_rcPushedTextPadding.bottom != 0 || m_rcPushedTextPadding.left != 0 || m_rcPushedTextPadding.right != 0 || m_rcPushedTextPadding.top != 0))
	{
		CDuiString sText = GetText();
		if (sText.IsEmpty()) return;
		DWORD clrColor = IsEnabled() ? m_dwTextColor : m_dwDisabledTextColor;

		if (GetPushedTextColor() != 0)
			clrColor = GetPushedTextColor();

		RECT rc = m_rcItem;
		rc.left += m_rcPushedTextPadding.left;
		rc.right -= m_rcPushedTextPadding.right;
		rc.top += m_rcPushedTextPadding.top;
		rc.bottom -= m_rcPushedTextPadding.bottom;

		CRenderEngine::DrawText(hDC, m_pManager, rc, sText, clrColor, \
			m_iFont, m_uTextStyle);
	}
	else
		CButtonUI::PaintText(hDC);
}